/**
 * This is for ROA SDK 
 */

import Util;
import Credential;
import ROAUtil;

type @protocol = string
type @readTimeout = number
type @connectTimeout = number
type @httpProxy = string
type @httpsProxy = string
type @noProxy = string
type @maxIdleConns = number
type @endpointHost = string
type @network = string
type @endpointRule = string
type @endpointMap = map[string]string
type @suffix = string
type @productId = string
type @regionId = string
type @userAgent = string
type @sourceIp = string
type @secureTransport = string
type @credential = Credential

/**
 * Model for initing client
 */
model Config {
  accessKeyId?: string(description='accesskey id',default=''),
  accessKeySecret?: string(description='accesskey secret',default=''),
  securityToken?: string(description='security token',default=''),
  protocol?: string(description='http protocol',example='http',default='http'),
  regionId?: string(description='region id',example='cn-hangzhou',default='',pattern='^[a-zA-Z0-9_-]+$'),
  readTimeout?: number(description='read timeout',example='10',default=''),
  connectTimeout?: number(description='connect timeout',example='10',default=''),
  httpProxy?: string(description='http proxy',example='http://localhost',default=''),
  httpsProxy?: string(description='https proxy',example='https://localhost',default=''),
  credential?: Credential(description='credential',example='',default=''),
  endpoint?: string(description='endpoint',example='cs.aliyuncs.com',default=''),
  noProxy?: string(description='proxy white list',example='http://localhost',default=''),
  userAgent?: string(description='user agent',example='Alibabacloud/1',default=''),
  maxIdleConns?: number(description='max idle conns',example='3',default=''),
  network?: string(description='network for endpoint',example='public',default='',pattern='^[a-zA-Z0-9_-]+$'),
  suffix?: string(description='suffix for endpoint',example='aliyun',default='',pattern='^[a-zA-Z0-9_-]+$'),
  type?: string(description='credential type',example='access_key',default='',deprecated=true),
  sourceIp?: string(description='source ip', example='192.168.0.1', default=''),
  secureTransport?: string(description='secure transport', example='true', default='false')
}

/**
 * Init client with Config
 * @param config config contains the necessary information to create a client
 */
init(config: Config) {
  if (Util.isUnset(config)) {
    throw {
      code = 'ParameterMissing',
      message = '\'config\' can not be unset'
    };
  }

  Util.validateModel(config);

  if(!Util.empty(config.accessKeyId) && !Util.empty(config.accessKeySecret)){
    if (!Util.empty(config.securityToken)) {
      config.type = 'sts';
    } else {
      config.type = 'access_key';
    }
    var credentialConfig = new Credential.Config{
      accessKeyId = config.accessKeyId,
      type = config.type,
      accessKeySecret = config.accessKeySecret,
      securityToken = config.securityToken
    };
    @credential = new Credential(credentialConfig);
  } else if(!Util.isUnset(config.credential)) {
    @credential = config.credential;
  } else {
    throw {
      code = 'ParameterMissing',
      message = '\'accessKeyId\' and \'accessKeySecret\' or \'credential\' can not be unset'
    };
  }
  @sourceIp = config.sourceIp;
  @secureTransport = config.secureTransport;
  @regionId = config.regionId;
  @protocol = config.protocol;
  @endpointHost = config.endpoint;
  @readTimeout = config.readTimeout;
  @connectTimeout = config.connectTimeout;
  @httpProxy = config.httpProxy;
  @httpsProxy = config.httpsProxy;
  @maxIdleConns = config.maxIdleConns;
}


/**
 * Encapsulate the request and invoke the network
 * @param version product version
 * @param protocol http or https
 * @param method e.g. GET
 * @param authType when authType is Anonymous, the signature will not be calculate
 * @param pathname pathname of every api
 * @param query which contains request params
 * @param headers request headers
 * @param body content of request
 * @param runtime which controls some details of call api, such as retry times
 * @return the response
 */
api doRequest(version: string, protocol: string, method: string, authType: string, pathname: string, query: map[string]string, headers: map[string]string, body: any, runtime: Util.RuntimeOptions): object {
  __request.protocol = Util.defaultString(@protocol, protocol);
  __request.method = method;
  __request.pathname = pathname;
  __request.headers = {
    date = Util.getDateUTCString(),
    host = @endpointHost,
    accept = 'application/json',
    x-acs-signature-nonce = Util.getNonce(),
    x-acs-signature-method = 'HMAC-SHA1',
    x-acs-signature-version = '1.0',
    x-acs-version = version,
    user-agent = Util.getUserAgent(@userAgent),
    // x-sdk-client': helper.DEFAULT_CLIENT
    ...headers
  };
  if (!Util.isUnset(@sourceIp)) {
    __request.headers.x-acs-source-ip = @sourceIp;
  }
  if (!Util.isUnset(@secureTransport)) {
    __request.headers.x-acs-secure-transport = @secureTransport;
  }
  if (!Util.isUnset(body)) {
    __request.body = Util.toJSONString(body);
    __request.headers.content-type = 'application/json; charset=utf-8';
  }

  if (!Util.isUnset(query)) {
    __request.query = query;
  }

  if (!Util.equalString(authType, 'Anonymous')) {
    var accessKeyId = @credential.getAccessKeyId();
    var accessKeySecret = @credential.getAccessKeySecret();
    var securityToken = @credential.getSecurityToken();
    if (!Util.empty(securityToken)) {
      __request.headers.x-acs-accesskey-id = accessKeyId;
      __request.headers.x-acs-security-token = securityToken;
    }

    var stringToSign = ROAUtil.getStringToSign(__request);
    __request.headers.authorization = `acs ${accessKeyId}:${ROAUtil.getSignature(stringToSign, accessKeySecret)}`;
  }
} returns {
  if (Util.equalNumber(__response.statusCode, 204)) {
    return {
      headers = __response.headers
    };
  }
  var result = Util.readAsJSON(__response.body);

  if (Util.is4xx(__response.statusCode) || Util.is5xx(__response.statusCode)) {
    var err = Util.assertAsMap(result);
    throw {
      code = `${defaultAny(err.Code, err.code)}`,
      message = `code: ${__response.statusCode}, ${defaultAny(err.Message, err.message)} request id: ${defaultAny(err.RequestId, err.requestId)}`,
      data = err,
    }
  }
  return {
    headers = __response.headers,
    body = result
  };
} runtime {
  timeouted = 'retry',
  readTimeout = Util.defaultNumber(runtime.readTimeout, @readTimeout),
  connectTimeout = Util.defaultNumber(runtime.connectTimeout, @connectTimeout),
  httpProxy = Util.defaultString(runtime.httpProxy, @httpProxy),
  httpsProxy = Util.defaultString(runtime.httpsProxy, @httpsProxy),
  noProxy = Util.defaultString(runtime.noProxy, @noProxy),
  maxIdleConns = Util.defaultNumber(runtime.maxIdleConns, @maxIdleConns),
  retry = {
    retryable = runtime.autoretry,
    maxAttempts = Util.defaultNumber(runtime.maxAttempts, 3)
  },
  backoff = {
    policy = Util.defaultString(runtime.backoffPolicy, 'no'),
    period = Util.defaultNumber(runtime.backoffPeriod, 1)
  },
  ignoreSSL = runtime.ignoreSSL
}

/**
 * Encapsulate the request and invoke the network
 * @param action api name
 * @param version product version
 * @param protocol http or https
 * @param method e.g. GET
 * @param authType when authType is Anonymous, the signature will not be calculate
 * @param pathname pathname of every api
 * @param query which contains request params
 * @param headers request headers
 * @param body content of request
 * @param runtime which controls some details of call api, such as retry times
 * @return the response
 */
api doRequestWithAction(action: string, version: string, protocol: string, method: string, authType: string, pathname: string, query: map[string]string, headers: map[string]string, body: any, runtime: Util.RuntimeOptions): object {
  __request.protocol = Util.defaultString(@protocol, protocol);
  __request.method = method;
  __request.pathname = pathname;
  __request.headers = {
    date = Util.getDateUTCString(),
    host = @endpointHost,
    accept = 'application/json',
    x-acs-signature-nonce = Util.getNonce(),
    x-acs-signature-method = 'HMAC-SHA1',
    x-acs-signature-version = '1.0',
    x-acs-version = version,
    x-acs-action = action,
    user-agent = Util.getUserAgent(@userAgent),
    // x-sdk-client': helper.DEFAULT_CLIENT
    ...headers
  };
  if (!Util.isUnset(@sourceIp)) {
    __request.headers.x-acs-source-ip = @sourceIp;
  }
  if (!Util.isUnset(@secureTransport)) {
    __request.headers.x-acs-secure-transport = @secureTransport;
  }
  if (!Util.isUnset(body)) {
    __request.body = Util.toJSONString(body);
    __request.headers.content-type = 'application/json; charset=utf-8';
  }

  if (!Util.isUnset(query)) {
    __request.query = query;
  }

  if (!Util.equalString(authType, 'Anonymous')) {
    var accessKeyId = @credential.getAccessKeyId();
    var accessKeySecret = @credential.getAccessKeySecret();
    var securityToken = @credential.getSecurityToken();
    if (!Util.empty(securityToken)) {
      __request.headers.x-acs-accesskey-id = accessKeyId;
      __request.headers.x-acs-security-token = securityToken;
    }

    var stringToSign = ROAUtil.getStringToSign(__request);
    __request.headers.authorization = `acs ${accessKeyId}:${ROAUtil.getSignature(stringToSign, accessKeySecret)}`;
  }
} returns {
  if (Util.equalNumber(__response.statusCode, 204)) {
    return {
      headers = __response.headers
    };
  }
  var result = Util.readAsJSON(__response.body);

  if (Util.is4xx(__response.statusCode) || Util.is5xx(__response.statusCode)) {
    var err = Util.assertAsMap(result);
    throw {
      code = `${defaultAny(err.Code, err.code)}`,
      message = `code: ${__response.statusCode}, ${defaultAny(err.Message, err.message)} request id: ${defaultAny(err.RequestId, err.requestId)}`,
      data = err,
    }
  }
  return {
    headers = __response.headers,
    body = result
  };
} runtime {
  timeouted = 'retry',
  readTimeout = Util.defaultNumber(runtime.readTimeout, @readTimeout),
  connectTimeout = Util.defaultNumber(runtime.connectTimeout, @connectTimeout),
  httpProxy = Util.defaultString(runtime.httpProxy, @httpProxy),
  httpsProxy = Util.defaultString(runtime.httpsProxy, @httpsProxy),
  noProxy = Util.defaultString(runtime.noProxy, @noProxy),
  maxIdleConns = Util.defaultNumber(runtime.maxIdleConns, @maxIdleConns),
  retry = {
    retryable = runtime.autoretry,
    maxAttempts = Util.defaultNumber(runtime.maxAttempts, 3)
  },
  backoff = {
    policy = Util.defaultString(runtime.backoffPolicy, 'no'),
    period = Util.defaultNumber(runtime.backoffPeriod, 1)
  },
  ignoreSSL = runtime.ignoreSSL
}

/**
 * Encapsulate the request and invoke the network
 * @param version product version
 * @param protocol http or https
 * @param method e.g. GET
 * @param authType when authType is Anonymous, the signature will not be calculate
 * @param pathname pathname of every api
 * @param query which contains request params
 * @param headers request headers
 * @param body content of request
 * @param runtime which controls some details of call api, such as retry times
 * @return the response
 */
api doRequestWithForm(version: string, protocol: string, method: string, authType: string, pathname: string, query: map[string]string, headers: map[string]string, body: map[string]any, runtime: Util.RuntimeOptions): object {
  __request.protocol = Util.defaultString(@protocol, protocol);
  __request.method = method;
  __request.pathname = pathname;
  __request.headers = {
    date = Util.getDateUTCString(),
    host = @endpointHost,
    accept = 'application/json',
    x-acs-signature-nonce = Util.getNonce(),
    x-acs-signature-method = 'HMAC-SHA1',
    x-acs-signature-version = '1.0',
    x-acs-version = version,
    user-agent = Util.getUserAgent(@userAgent),
    // x-sdk-client': helper.DEFAULT_CLIENT
    ...headers
  };
  if (!Util.isUnset(@sourceIp)) {
    __request.headers.x-acs-source-ip = @sourceIp;
  }
  if (!Util.isUnset(@secureTransport)) {
    __request.headers.x-acs-secure-transport = @secureTransport;
  }
  if (!Util.isUnset(body)) {
    __request.body = ROAUtil.toForm(body);
    __request.headers.content-type = 'application/x-www-form-urlencoded';
  }

  if (!Util.isUnset(query)) {
    __request.query = query;
  }

  if (!Util.equalString(authType, 'Anonymous')) {
    var accessKeyId = @credential.getAccessKeyId();
    var accessKeySecret = @credential.getAccessKeySecret();
    var securityToken = @credential.getSecurityToken();
    if (!Util.empty(securityToken)) {
      __request.headers.x-acs-accesskey-id = accessKeyId;
      __request.headers.x-acs-security-token = securityToken;
    }

    var stringToSign = ROAUtil.getStringToSign(__request);
    __request.headers.authorization = `acs ${accessKeyId}:${ROAUtil.getSignature(stringToSign, accessKeySecret)}`;
  }
} returns {
  if (Util.equalNumber(__response.statusCode, 204)) {
    return {
      headers = __response.headers
    };
  }
  var result = Util.readAsJSON(__response.body);

  if (Util.is4xx(__response.statusCode) || Util.is5xx(__response.statusCode)) {
    var err = Util.assertAsMap(result);
    throw {
      code = `${defaultAny(err.Code, err.code)}`,
      message = `code: ${__response.statusCode}, ${defaultAny(err.Message, err.message)} request id: ${defaultAny(err.RequestId, err.requestId)}`,
      data = err,
    }
  }
  return {
    headers = __response.headers,
    body = result
  };
} runtime {
  timeouted = 'retry',
  readTimeout = Util.defaultNumber(runtime.readTimeout, @readTimeout),
  connectTimeout = Util.defaultNumber(runtime.connectTimeout, @connectTimeout),
  httpProxy = Util.defaultString(runtime.httpProxy, @httpProxy),
  httpsProxy = Util.defaultString(runtime.httpsProxy, @httpsProxy),
  noProxy = Util.defaultString(runtime.noProxy, @noProxy),
  maxIdleConns = Util.defaultNumber(runtime.maxIdleConns, @maxIdleConns),
  retry = {
    retryable = runtime.autoretry,
    maxAttempts = Util.defaultNumber(runtime.maxAttempts, 3)
  },
  backoff = {
    policy = Util.defaultString(runtime.backoffPolicy, 'no'),
    period = Util.defaultNumber(runtime.backoffPeriod, 1)
  },
  ignoreSSL = runtime.ignoreSSL
}

/**
 * If inputValue is not null, return it or return defaultValue
 * @param inputValue  users input value
 * @param defaultValue default value
 * @return the final result
 */
static function defaultAny(inputValue: any, defaultValue: any): any {
  if (Util.isUnset(inputValue)) {
    return defaultValue;
  }
  return inputValue;
}

/**
 * If the endpointRule and config.endpoint are empty, throw error
 * @param config config contains the necessary information to create a client
 */
function checkConfig(config: Config)throws : void {
  if (Util.empty(@endpointRule) && Util.empty(config.endpoint)) {
    throw {
      code = 'ParameterMissing',
      message = '\'config.endpoint\' can not be empty'
    };
  }
}